#ifndef __RDISTRO_DISTRIBUTION_SESSION_H__
#define __RDISTRO_DISTRIBUTION_SESSION_H__

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <time.h>

#include "rdistro.h"
#include "../tdp2/tdp2_hash.hpp"

namespace RDistro
{
	class DistributionSession
	{
	public:
		struct JobKey
		{
			UInt8 data[32];
			UInt16 jobTypeID;
		};

	private:
		clock_t _lastActivity;
		SOCKET _socket;
		bool _socketActive;
		bool _connectionActive;
		UInt8 _authenticationToken[32];
		UInt8 _loginKey[32];
		bool _loggedIn;
		int _connectionState;
		UInt8 _packetData[65536];

		UInt16 _ppProtocolID;
		UInt16 _ppSize;
		bool _ppWaiting;
		char _pendingFileName[4096];
		FILE *_activeFile;
		UInt8 _activeTimestamp;
		int _activeCompareResult;
		JobKey _activeJobKey;
		UInt32 _activeWorkFirstUnit;
		UInt32 _activeWorkNumUnits;
		char _activeTarget[4096];

		void Initialize(const char *hostname, const void *loginKey, size_t loginKeySize);
		bool FileTimestamp(const char *fname, UInt8 timestamp[8]);
		bool BlockOnState(int state);

	public:
		enum
		{
			CS_Disconnected,
			CS_Connecting,
			CS_WaitingForFeedback,
			CS_Idle,
			CS_SynchronizingFile,
			CS_UploadingFile,
			CS_DownloadingFile,
			CS_OperationFailed,
			CS_ConnectionDropped,
		};

		enum
		{
			JS_Finished,
			JS_Finishing,
			JS_Outstanding,
		};

		DistributionSession(const char *hostname, const void *loginKey, size_t loginKeySize);
		DistributionSession(const char *hostname, const char *loginKey);
		virtual ~DistributionSession();

		virtual void SendPacket(UInt16 protocolCode, const void *bytes, size_t numBytes);

		virtual void ProcessPacket(int protocolID, const void *data, size_t size);
		virtual void ProcessData();
		virtual bool LoggedIn();
		virtual bool ConnectionActive();
		virtual int ConnectionState();
		virtual void SetConnectionState(int state);
		
		virtual bool UploadFile(const char *filename);
		virtual bool SynchronizeFile(const char *filename, bool allowUpload = true, bool allowDownload = true, bool *updated = NULL);
		virtual bool DownloadFile(const char *filename);
		virtual bool RequestJob(JobKey &jobKey, char activeTarget[4096]);
		virtual bool RequestJobStatus(JobKey &jobKey, int &status);
		virtual bool RequestWork(const JobKey &jobKey, UInt32 &firstUnit, UInt32 &numUnits);
		virtual void CreateJob(const JobKey &jobKey, UInt32 numUnits, UInt8 granularity, const char *target);
		virtual void DestroyJob(const JobKey &jobKey);
		virtual JobKey CreateRandomJobKey(UInt16 jobTypeID);
	};

	typedef DistributionSession *(*PFN_CreateDistributionSession_StringKey) (const char *hostname, const char *key);
	typedef DistributionSession *(*PFN_CreateDistributionSession_DataKey) (const char *hostname, const void *str, size_t len);
	typedef void (*PFN_DestroyDistributionSession) (DistributionSession *session);

	extern "C" DistributionSession *CreateDistributionSession_StringKey(const char *hostname, const char *key);
	extern "C" DistributionSession *CreateDistributionSession_DataKey(const char *hostname, const void *str, size_t len);
	extern "C" void DestroyDistributionSession(DistributionSession *session);
};

#endif
